namespace ts {
    let NodeConstructor: new (kind: 语法, pos: number, end: number) => Node;
    let TokenConstructor: new (kind: 语法, pos: number, end: number) => Node;
    let IdentifierConstructor: new (kind: 语法, pos: number, end: number) => Node;
    let SourceFileConstructor: new (kind: 语法, pos: number, end: number) => Node;

    export function createNode(kind: 语法, pos?: number, end?: number): Node {
        if (kind === 语法.SourceFile) {
            return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
        }
        else if (kind === 语法.id) {
            return new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, pos, end);
        }
        else if (kind < 语法.Assert) {
            return new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, pos, end);
        }
        else {
            return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, pos, end);
        }
    }

    function visitNode<T>(cbNode: (node: Node) => T, node: Node): T {
        if (node) {
            return cbNode(node);
        }
    }

    function visitNodeArray<T>(cbNodes: (nodes: Node[]) => T, nodes: Node[]) {
        if (nodes) {
            return cbNodes(nodes);
        }
    }

    function visitEachNode<T>(cbNode: (node: Node) => T, nodes: Node[]) {
        if (nodes) {
            for (const node of nodes) {
                const result = cbNode(node);
                if (result) {
                    return result;
                }
            }
        }
    }

    // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
    // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
    // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
    // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
    export function forEachChild<T>(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T {
        if (!node) {
            return;
        }
        // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray
        // callback parameters, but that causes a closure allocation for each invocation with noticeable effects
        // on performance.
        const visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
        const cbNodes = cbNodeArray || cbNode;

    }

    export function createSourceFile(fileName: string, sourceText: string,  setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
        performance.mark("beforeParse");
        const result = Parser.parseSourceFile(fileName, sourceText, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
        performance.mark("afterParse");
        performance.measure("Parse", "beforeParse", "afterParse");
        return result;
    }

    export function isExternalModule(file: SourceFile): boolean {
        return file.externalModuleIndicator !== undefined;
    }

    // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
    // indicates what changed between the 'text' that this SourceFile has and the 'newText'.
    // The SourceFile will be created with the compiler attempting to reuse as many nodes from
    // this file as possible.
    //
    // Note: this function mutates nodes from this SourceFile. That means any existing nodes
    // from this SourceFile that are being held onto may change as a result (including
    // becoming detached from any SourceFile).  It is recommended that this SourceFile not
    // be used once 'update' is called on it.
    export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile {
        return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
    }

    // Implement the parser as a singleton module.  We do this for perf reasons because creating
    // parser instances can actually be expensive enough to impact us on projects with many source
    // files.
    namespace Parser {
        // Share a single scanner across all calls to parse a source file.  This helps speed things
        // up by avoiding the cost of creating/compiling scanners over and over again.
        const scanner = createScanner();
        const disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext;

        // capture constructors in 'initializeState' to avoid null checks
        let NodeConstructor: new (kind: 语法, pos: number, end: number) => Node;
        let TokenConstructor: new (kind: 语法, pos: number, end: number) => Node;
        let IdentifierConstructor: new (kind: 语法, pos: number, end: number) => Node;
        let SourceFileConstructor: new (kind: 语法, pos: number, end: number) => Node;

        let sourceFile: SourceFile;
        let parseDiagnostics: Diagnostic[];
        let syntaxCursor: IncrementalParser.SyntaxCursor;

        let currentToken: 语法;
        let sourceText: string;
        let nodeCount: number;
        let identifiers: Map<string>;
        let identifierCount: number;

        let parsingContext: ParsingContext;

        // Flags that dictate what parsing context we're in.  For example:
        // Whether or not we are in strict parsing mode.  All that changes in strict parsing mode is
        // that some tokens that would be considered identifiers may be considered keywords.
        //
        // When adding more parser context flags, consider which is the more common case that the
        // flag will be in.  This should be the 'false' state for that flag.  The reason for this is
        // that we don't store data in our nodes unless the value is in the *non-default* state.  So,
        // for example, more often than code 'allows-in' (or doesn't 'disallow-in').  We opt for
        // 'disallow-in' set to 'false'.  Otherwise, if we had 'allowsIn' set to 'true', then almost
        // all nodes would need extra state on them to store this info.
        //
        // Note:  'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6
        // grammar specification.
        //
        // An important thing about these context concepts.  By default they are effectively inherited
        // while parsing through every grammar production.  i.e. if you don't change them, then when
        // you parse a sub-production, it will have the same context values as the parent production.
        // This is great most of the time.  After all, consider all the 'expression' grammar productions
        // and how nearly all of them pass along the 'in' and 'yield' context values:
        //
        // EqualityExpression[In, Yield] :
        //      RelationalExpression[?In, ?Yield]
        //      EqualityExpression[?In, ?Yield] == RelationalExpression[?In, ?Yield]
        //      EqualityExpression[?In, ?Yield] != RelationalExpression[?In, ?Yield]
        //      EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield]
        //      EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield]
        //
        // Where you have to be careful is then understanding what the points are in the grammar
        // where the values are *not* passed along.  For example:
        //
        // SingleNameBinding[Yield,GeneratorParameter]
        //      [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt
        //      [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt
        //
        // Here this is saying that if the GeneratorParameter context flag is set, that we should
        // explicitly set the 'yield' context flag to false before calling into the BindingIdentifier
        // and we should explicitly unset the 'yield' context flag before calling into the Initializer.
        // production.  Conversely, if the GeneratorParameter context flag is not set, then we
        // should leave the 'yield' context flag alone.
        //
        // Getting this all correct is tricky and requires careful reading of the grammar to
        // understand when these values should be changed versus when they should be inherited.
        //
        // Note: it should not be necessary to save/restore these flags during speculative/lookahead
        // parsing.  These context flags are naturally stored and restored through normal recursive
        // descent parsing and unwinding.
        let contextFlags: NodeFlags;

        // Whether or not we've had a parse error since creating the last AST node.  If we have
        // encountered an error, it will be stored on the next AST node we create.  Parse errors
        // can be broken down into three categories:
        //
        // 1) An error that occurred during scanning.  For example, an unterminated literal, or a
        //    character that was completely not understood.
        //
        // 2) A token was expected, but was not present.  This type of error is commonly produced
        //    by the 'parseExpected' function.
        //
        // 3) A token was present that no parsing function was able to consume.  This type of error
        //    only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
        //    decides to skip the token.
        //
        // In all of these cases, we want to mark the next node as having had an error before it.
        // With this mark, we can know in incremental settings if this node can be reused, or if
        // we have to reparse it.  If we don't keep this information around, we may just reuse the
        // node.  in that event we would then not produce the same errors as we did before, causing
        // significant confusion problems.
        //
        // Note: it is necessary that this value be saved/restored during speculative/lookahead
        // parsing.  During lookahead parsing, we will often create a node.  That node will have
        // this value attached, and then this value will be set back to 'false'.  If we decide to
        // rewind, we must get back to the same value we had prior to the lookahead.
        //
        // Note: any errors at the end of the file that do not precede a regular node, should get
        // attached to the EOF token.
        let parseErrorBeforeNextFinishedNode = false;

        export function parseSourceFile(fileName: string, _sourceText: string,  _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile {
            scriptKind = ensureScriptKind(fileName, scriptKind);

            initializeState(fileName, _sourceText, _syntaxCursor, scriptKind);

            const result = parseSourceFileWorker(fileName, setParentNodes, scriptKind);

            clearState();

            return result;
        }

        function initializeState(fileName: string, _sourceText: string,  _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) {
            NodeConstructor = objectAllocator.getNodeConstructor();
            TokenConstructor = objectAllocator.getTokenConstructor();
            IdentifierConstructor = objectAllocator.getIdentifierConstructor();
            SourceFileConstructor = objectAllocator.getSourceFileConstructor();

            sourceText = _sourceText;
            syntaxCursor = _syntaxCursor;

            parseDiagnostics = [];
            parsingContext = 0;
            identifiers = new Map<string, string>();
            identifierCount = 0;
            nodeCount = 0;

            contextFlags = NodeFlags.None;
            parseErrorBeforeNextFinishedNode = false;

            // Initialize and prime the scanner before parsing the source elements.
            scanner.setText(sourceText);
            scanner.setOnError(scanError);
        }

        function clearState() {
            // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily.
            scanner.setText("");
            scanner.setOnError(undefined);

            // Clear any data.  We don't want to accidentally hold onto it for too long.
            parseDiagnostics = undefined;
            sourceFile = undefined;
            identifiers = undefined;
            syntaxCursor = undefined;
            sourceText = undefined;
        }

        function parseSourceFileWorker(fileName: string, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile {
            sourceFile = createSourceFile(fileName, scriptKind);
            sourceFile.flags = contextFlags;

            // Prime the scanner.
            nextToken();

            sourceFile.statements = ParseObjectList()
            Debug.assert(token() === 语法.Eof);
            sourceFile.endOfFileToken = parseTokenNode();

            sourceFile.nodeCount = nodeCount;
            sourceFile.identifierCount = identifierCount;
            sourceFile.identifiers = identifiers;
            sourceFile.parseDiagnostics = parseDiagnostics;

            if (setParentNodes) {
                fixupParentReferences(sourceFile);
            }

            return sourceFile;
        }

        function ParseObjectList() {
            return parseList(ParsingContext.SourceElements, ParseObject);
        }


        export function fixupParentReferences(rootNode: Node) {
            // normally parent references are set during binding. However, for clients that only need
            // a syntax tree, and no semantic features, then the binding process is an unnecessary
            // overhead.  This functions allows us to set all the parents, without all the expense of
            // binding.

            let parent: Node = rootNode;
            forEachChild(rootNode, visitNode);
            return;

            function visitNode(n: Node): void {
                // walk down setting parents that differ from the parent we think it should be.  This
                // allows us to quickly bail out of setting parents for subtrees during incremental
                // parsing
                if (n.parent !== parent) {
                    n.parent = parent;

                    const saveParent = parent;
                    parent = n;
                    forEachChild(n, visitNode);
                    if (n.jsDocComments) {
                        for (const jsDocComment of n.jsDocComments) {
                            jsDocComment.parent = n;
                            parent = jsDocComment;
                            forEachChild(jsDocComment, visitNode);
                        }
                    }
                    parent = saveParent;
                }
            }
        }

        function createSourceFile(fileName: string, scriptKind: ScriptKind): SourceFile {
            // code from createNode is inlined here so createNode won't have to deal with special case of creating source files
            // this is quite rare comparing to other nodes and createNode should be as fast as possible
            const sourceFile = <SourceFile>new SourceFileConstructor(语法.SourceFile, /*pos*/ 0, /* end */ sourceText.length);
            nodeCount++;

            sourceFile.text = sourceText;
            sourceFile.bindDiagnostics = [];
            sourceFile.fileName = normalizePath(fileName);
            sourceFile.scriptKind = scriptKind;

            return sourceFile;
        }

        function setContextFlag(val: boolean, flag: NodeFlags) {
            if (val) {
                contextFlags |= flag;
            }
            else {
                contextFlags &= ~flag;
            }
        }

        function doOutsideOfContext<T>(context: NodeFlags, func: () => T): T {
            // contextFlagsToClear will contain only the context flags that are
            // currently set that we need to temporarily clear
            // We don't just blindly reset to the previous flags to ensure
            // that we do not mutate cached flags for the incremental
            // parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and
            // HasAggregatedChildData).
            const contextFlagsToClear = context & contextFlags;
            if (contextFlagsToClear) {
                // clear the requested context flags
                setContextFlag(/*val*/ false, contextFlagsToClear);
                const result = func();
                // restore the context flags we just cleared
                setContextFlag(/*val*/ true, contextFlagsToClear);
                return result;
            }

            // no need to do anything special as we are not in any of the requested contexts
            return func();
        }

        function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): void {
            const start = scanner.getTokenPos();
            const length = scanner.getTextPos() - start;

            parseErrorAtPosition(start, length, message, arg0);
        }

        function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): void {
            // Don't report another error if it would just be at the same position as the last error.
            const lastError = lastOrUndefined(parseDiagnostics);
            if (!lastError || start !== lastError.start) {
                parseDiagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0));
            }

            // Mark that we've encountered an error.  We'll set an appropriate bit on the next
            // node we finish so that it can't be reused incrementally.
            parseErrorBeforeNextFinishedNode = true;
        }

        function scanError(message: DiagnosticMessage, length?: number) {
            const pos = scanner.getTextPos();
            parseErrorAtPosition(pos, length || 0, message);
        }

        function getNodePos(): number {
            return scanner.getStartPos();
        }

        function getNodeEnd(): number {
            return scanner.getStartPos();
        }

        // Use this function to access the current token instead of reading the currentToken
        // variable. Since function results aren't narrowed in control flow analysis, this ensures
        // that the type checker doesn't make wrong assumptions about the type of the current
        // token (e.g. a call to nextToken() changes the current token but the checker doesn't
        // reason about this side effect).  Mainstream VMs inline simple functions like this, so
        // there is no performance penalty.
        function token(): 语法 {
            return currentToken;
        }

        function nextToken(): 语法 {
            return currentToken = scanner.scan();
        }

        function speculationHelper<T>(callback: () => T, isLookAhead: boolean): T {
            // Keep track of the state we'll need to rollback to if lookahead fails (or if the
            // caller asked us to always reset our state).
            const saveToken = currentToken;
            const saveParseDiagnosticsLength = parseDiagnostics.length;
            const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;

            // Note: it is not actually necessary to save/restore the context flags here.  That's
            // because the saving/restoring of these flags happens naturally through the recursive
            // descent nature of our parser.  However, we still store this here just so we can
            // assert that invariant holds.
            const saveContextFlags = contextFlags;

            // If we're only looking ahead, then tell the scanner to only lookahead as well.
            // Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
            // same.
            const result = isLookAhead
                ? scanner.lookAhead(callback)
                : scanner.tryScan(callback);

            Debug.assert(saveContextFlags === contextFlags);

            // If our callback returned something 'falsy' or we're just looking ahead,
            // then unconditionally restore us to where we were.
            if (!result || isLookAhead) {
                currentToken = saveToken;
                parseDiagnostics.length = saveParseDiagnosticsLength;
                parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode;
            }

            return result;
        }

        /** Invokes the provided callback then unconditionally restores the parser to the state it
         * was in immediately prior to invoking the callback.  The result of invoking the callback
         * is returned from this function.
         */
        function lookAhead<T>(callback: () => T): T {
            return speculationHelper(callback, /*isLookAhead*/ true);
        }

        /** Invokes the provided callback.  If the callback returns something falsy, then it restores
         * the parser to the state it was in immediately prior to invoking the callback.  If the
         * callback returns something truthy, then the parser state is not rolled back.  The result
         * of invoking the callback is returned from this function.
         */
        function tryParse<T>(callback: () => T): T {
            return speculationHelper(callback, /*isLookAhead*/ false);
        }



        function parseExpected(kind: 语法, diagnosticMessage?: DiagnosticMessage, shouldAdvance = true): boolean {
            if (token() === kind) {
                if (shouldAdvance) {
                    nextToken();
                }
                return true;
            }

            // Report specific message if provided with one.  Otherwise, report generic fallback message.
            if (diagnosticMessage) {
                parseErrorAtCurrentToken(diagnosticMessage);
            }
            else {
                parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind));
            }
            return false;
        }

        function parseOptional(t: 语法): boolean {
            if (token() === t) {
                nextToken();
                return true;
            }
            return false;
        }

        function parseTokenNode<T extends Node>(): T {
            const node = <T>createNode(token());
            nextToken();
            return finishNode(node);
        }

        function canParseSemicolon() {
            // If there's a real semicolon, then we can always parse it out.
            if (token() === 语法.semi) {
                return true;
            }

            // We can parse out an optional semicolon in ASI cases in the following cases.
            return token() === 语法.r_brace || token() === 语法.Eof || scanner.hasPrecedingLineBreak();
        }

        function parseSemicolon(): boolean {
            if (canParseSemicolon()) {
                if (token() === 语法.semi) {
                    // consume the semicolon if it was explicitly provided.
                    nextToken();
                }

                return true;
            }
            else {
                return parseExpected(语法.semi);
            }
        }

        // note: this function creates only node
        function createNode(kind: 语法, pos?: number): Node | Token | Identifier {
            nodeCount++;
            if (!(pos >= 0)) {
                pos = scanner.getStartPos();
            }

            return kind >= 语法.数字字面量 ? new NodeConstructor(kind, pos, pos) :
                kind === 语法.id ? new IdentifierConstructor(kind, pos, pos) :
                    new TokenConstructor(kind, pos, pos);
        }

        function finishNode<T extends Node>(node: T, end?: number): T {
            node.end = end === undefined ? scanner.getStartPos() : end;

            if (contextFlags) {
                node.flags |= contextFlags;
            }

            // Keep track on the node if we encountered an error while parsing it.  If we did, then
            // we cannot reuse the node incrementally.  Once we've marked this node, clear out the
            // flag so that we don't mark any subsequent nodes.
            if (parseErrorBeforeNextFinishedNode) {
                parseErrorBeforeNextFinishedNode = false;
                node.flags |= NodeFlags.ThisNodeHasError;
            }

            return node;
        }

        function createMissingNode(kind: 语法, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node {
            if (reportAtCurrentPosition) {
                parseErrorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0);
            }
            else {
                parseErrorAtCurrentToken(diagnosticMessage, arg0);
            }

            const result = createNode(kind, scanner.getStartPos());
            (<Identifier>result).text = "";
            return finishNode(result);
        }

        function internIdentifier(text: string): string {
            text = escapeIdentifier(text);
            if (!identifiers.has(text)) identifiers.set(text, text);
            return identifiers.get(text);
        }

        // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues
        // with magic property names like '__proto__'. The 'identifiers' object is used to share a single string instance for
        // each identifier in order to reduce memory consumption.
        function createIdentifier(isIdentifier: boolean, diagnosticMessage?: DiagnosticMessage): Identifier {
            identifierCount++;
            if (isIdentifier) {
                const node = <Identifier>createNode(语法.id);

                // Store original token kind if it is not just an Identifier so we can report appropriate error later in type checker
                if (token() !== 语法.id) {
                    node.originalKeywordKind = token();
                }
                node.text = internIdentifier(scanner.getTokenValue());
                nextToken();
                return finishNode(node);
            }

            return <Identifier>createMissingNode(语法.id, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics.Identifier_expected);
        }


        function parseIdentifierName(): Identifier {
            return createIdentifier(tokenIsIdentifierOrKeyword(token()));
        }


        // True if positioned at the start of a list element
        function 是列表元素(parsingContext: ParsingContext, inErrorRecovery: boolean): boolean {
            const node = currentNode(parsingContext);
            if (node) {
                return true;
            }
            switch (parsingContext) {
                case ParsingContext.SourceElements:
                    return 是语句开始();
                case ParsingContext.TypeParameters:
                case ParsingContext.TypeArguments:
                    return token() !== 语法.greater;
                case ParsingContext.ObjectLiteralMembers:
                    return token() !== 语法.r_brace;
                case ParsingContext.ArrayLiteralMembers:
                    return token() !== 语法.r_square;
                case ParsingContext.Arguments:
                    return token() !== 语法.r_paren;
            }
            return true;
        }

        function 是语句开始() {
            switch (token()) {
                case 语法.Assert:
                case 语法.Class:
                case 语法.Def:
                case 语法.Defm:
                case 语法.Defset:
                case 语法.Defvar:
                case 语法.Foreach:
                case 语法.If:
                case 语法.Let:
                case 语法.MultiClass:
                case 语法.Include:
                    return true;
                case 语法.Ifdef:
                case 语法.Ifndef:
                case 语法.Define:
                    return true;
            }
            return false;
        }


        // True if positioned at a list terminator
        function 是列表终止(kind: ParsingContext): boolean {
            if (token() === 语法.Eof) {
                // Being at the end of the file ends all lists.
                return true;
            }

            switch (kind) {
                case ParsingContext.SourceElements:
                    return token() === 语法.Eof || token() === 语法.r_brace;
                case ParsingContext.TypeParameters:
                case ParsingContext.TypeArguments:
                    return token() === 语法.greater;
                case ParsingContext.ObjectLiteralMembers:
                    return token() === 语法.r_brace;
                case ParsingContext.ArrayLiteralMembers:
                    return token() === 语法.r_square;
                case ParsingContext.Arguments:
                    return token() === 语法.r_paren;

            }
            return true
        }
        
        
        function createMissingList<T>(): NodeArray<T> {
            const pos = getNodePos();
            const result = <NodeArray<T>>[];
            result.pos = pos;
            result.end = pos;
            return result;
        }
        
        function 编译非逗号列表<T extends Node>(kind: ParsingContext, parseElement: () => T, open: 语法, close: 语法): NodeArray<T> {
            if (parseExpected(open)) {
                const result = parseList(kind, parseElement);
                parseExpected(close);
                return result;
            }
            return createMissingList<T>();
        }

        function parseBracketedList<T extends Node>(kind: ParsingContext, parseElement: () => T, open: 语法, close: 语法): NodeArray<T> {
            if (parseExpected(open)) {
                const result = parseDelimitedList(kind, parseElement);
                parseExpected(close);
                return result;
            }
            return createMissingList<T>();
        }

        // Parses a list of elements
        function parseList<T extends Node>(kind: ParsingContext, parseElement: () => T): NodeArray<T> {
            const saveParsingContext = parsingContext;
            parsingContext |= 1 << kind;
            const result = <NodeArray<T>>[];
            result.pos = getNodePos();

            while (!是列表终止(kind)) {
                if (是列表元素(kind, /*inErrorRecovery*/ false)) {
                    const element = parseListElement(kind, parseElement);
                    result.push(element);

                    continue;
                }

                if (abortParsingListOrMoveToNextToken(kind)) {
                    break;
                }
            }

            result.end = getNodeEnd();
            parsingContext = saveParsingContext;
            return result;
        }

        function parseListElement<T extends Node>(parsingContext: ParsingContext, parseElement: () => T): T {
            const node = currentNode(parsingContext);
            if (node) {
                return <T>consumeNode(node);
            }

            return parseElement();
        }

        function currentNode(parsingContext: ParsingContext): Node {
            // If there is an outstanding parse error that we've encountered, but not attached to
            // some node, then we cannot get a node from the old source tree.  This is because we
            // want to mark the next node we encounter as being unusable.
            //
            // Note: This may be too conservative.  Perhaps we could reuse the node and set the bit
            // on it (or its leftmost child) as having the error.  For now though, being conservative
            // is nice and likely won't ever affect perf.
            if (parseErrorBeforeNextFinishedNode) {
                return undefined;
            }

            if (!syntaxCursor) {
                // if we don't have a cursor, we could never return a node from the old tree.
                return undefined;
            }

            const node = syntaxCursor.currentNode(scanner.getStartPos());

            // Can't reuse a missing node.
            if (nodeIsMissing(node)) {
                return undefined;
            }

            // Can't reuse a node that intersected the change range.
            if (node.intersectsChange) {
                return undefined;
            }

            // Can't reuse a node that contains a parse error.  This is necessary so that we
            // produce the same set of errors again.
            if (containsParseError(node)) {
                return undefined;
            }

            // We can only reuse a node if it was parsed under the same strict mode that we're
            // currently in.  i.e. if we originally parsed a node in non-strict mode, but then
            // the user added 'using strict' at the top of the file, then we can't use that node
            // again as the presence of strict mode may cause us to parse the tokens in the file
            // differently.
            //
            // Note: we *can* reuse tokens when the strict mode changes.  That's because tokens
            // are unaffected by strict mode.  It's just the parser will decide what to do with it
            // differently depending on what mode it is in.
            //
            // This also applies to all our other context flags as well.
            const nodeContextFlags = node.flags & NodeFlags.ContextFlags;
            if (nodeContextFlags !== contextFlags) {
                return undefined;
            }

            // Ok, we have a node that looks like it could be reused.  Now verify that it is valid
            // in the current list parsing context that we're currently at.
            if (!canReuseNode(node, parsingContext)) {
                return undefined;
            }

            return node;
        }

        function consumeNode(node: Node) {
            // Move the scanner so it is after the node we just consumed.
            scanner.setTextPos(node.end);
            nextToken();
            return node;
        }

        function canReuseNode(node: Node, parsingContext: ParsingContext): boolean {
            return false;
        }


        function parsingContextErrors(context: ParsingContext): DiagnosticMessage {
            switch (context) {
                case ParsingContext.SourceElements: return Diagnostics.Declaration_or_statement_expected;
                case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected;
                case ParsingContext.TypeArguments: return Diagnostics.Type_parameter_declaration_expected;
                case ParsingContext.ObjectLiteralMembers: return Diagnostics.Type_parameter_declaration_expected;
                case ParsingContext.ArrayLiteralMembers: return Diagnostics.Type_parameter_declaration_expected;
                case ParsingContext.Arguments: return Diagnostics.Type_parameter_declaration_expected;
            }
        };

        // Parses a comma-delimited list of elements
        function parseDelimitedList<T extends Node>(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray<T> {
            const saveParsingContext = parsingContext;
            parsingContext |= 1 << kind;
            const result = <NodeArray<T>>[];
            result.pos = getNodePos();

            let commaStart = -1; // Meaning the previous token was not a comma
            while (true) {
                if (是列表元素(kind, /*inErrorRecovery*/ false)) {
                    result.push(parseListElement(kind, parseElement));
                    commaStart = scanner.getTokenPos();
                    if (parseOptional(语法.comma)) {
                        continue;
                    }

                    commaStart = -1; // Back to the state where the last token was not a comma
                    if (是列表终止(kind)) {
                        break;
                    }

                    // We didn't get a comma, and the list wasn't terminated, explicitly parse
                    // out a comma so we give a good error message.
                    parseExpected(语法.comma);

                    // If the token was a semicolon, and the caller allows that, then skip it and
                    // continue.  This ensures we get back on track and don't result in tons of
                    // parse errors.  For example, this can happen when people do things like use
                    // a semicolon to delimit object literal members.   Note: we'll have already
                    // reported an error when we called parseExpected above.
                    if (considerSemicolonAsDelimiter && token() === 语法.semi && !scanner.hasPrecedingLineBreak()) {
                        nextToken();
                    }
                    continue;
                }

                if (是列表终止(kind)) {
                    break;
                }

                if (abortParsingListOrMoveToNextToken(kind)) {
                    break;
                }
            }

            // Recording the trailing comma is deliberately done after the previous
            // loop, and not just if we see a list terminator. This is because the list
            // may have ended incorrectly, but it is still important to know if there
            // was a trailing comma.
            // Check if the last token was a comma.
            if (commaStart >= 0) {
                // Always preserve a trailing comma by marking it on the NodeArray
                result.hasTrailingComma = true;
            }

            result.end = getNodeEnd();
            parsingContext = saveParsingContext;
            return result;
        }

        // True if positioned at element or terminator of the current list or any enclosing list
        function isInSomeParsingContext(): boolean {
            for (let kind = 0; kind < ParsingContext.Count; kind++) {
                if (parsingContext & (1 << kind)) {
                    if (是列表元素(kind, /*inErrorRecovery*/ true) || 是列表终止(kind)) {
                        return true;
                    }
                }
            }

            return false;
        }
        
        // Returns true if we should abort parsing.
        function abortParsingListOrMoveToNextToken(kind: ParsingContext) {
            parsingContextErrors(kind);
            if (isInSomeParsingContext()) {
                return true;
            }

            nextToken();
            return false;
        }

        const enum ParsingContext {
            SourceElements,            // Elements in source file
            TypeParameters,
            TypeArguments,
            ObjectLiteralMembers,
            ArrayLiteralMembers,
            Arguments,
            Count                      // Number of parsing contexts
        }
        
        function 编译代码片段() {
            const node = createNode(语法.CodeFragment) as CodeFragment
            node.text = scanner.getTokenValue();
            if (scanner.hasExtendedUnicodeEscape()) {
                node.hasExtendedUnicodeEscape = true;
            }
            if (scanner.isUnterminated()) {
                node.isUnterminated = true;
            }
            nextToken();
            return finishNode(node);
        }

        function 编译文本字面量() {
            const node = createNode(语法.文本字面量) as StringLiteral
            node.text = scanner.getTokenValue();
            if (scanner.hasExtendedUnicodeEscape()) {
                node.hasExtendedUnicodeEscape = true;
            }
            if (scanner.isUnterminated()) {
                node.isUnterminated = true;
            }
            nextToken();
            return finishNode(node);
        }

        function 编译正负号数字() {
            const node = createNode(语法.一元表达式) as 一元表达式
            node.op = parseTokenNode()
            node.value1 = ParseValue();
            return finishNode(node);
        }

        function 编译数字字面量(): NumericLiteral {
            const node = createNode(语法.数字字面量) as NumericLiteral
            node.text = scanner.getTokenValue();
            node.numericLiteralFlags = scanner.getNumericLiteralFlags();
            if (scanner.hasExtendedUnicodeEscape()) {
                node.hasExtendedUnicodeEscape = true;
            }
            if (scanner.isUnterminated()) {
                node.isUnterminated = true;
            }
            nextToken();
            return finishNode(node);
        }

        function 编译包含语句() {
            const node = createNode(语法.导入) as 导入语句;
            parseExpected(语法.Include);
            node.文件名 = 编译文本字面量(); 
            finishNode(node);
            parseSemicolon();
            return node;
        }

        function 编译断言语句() {
            const node = createNode(语法.断言语句) as 断言语句
            parseExpected(语法.Assert);
            node.条件 = ParseValue();
            parseExpected(语法.comma)
            node.信息 = 编译文本字面量();
            finishNode(node);
            parseSemicolon();
            return node;
        }


        function 编译类型引用的类型参数() {
            if (!scanner.hasPrecedingLineBreak() && token() === 语法.less) {
                return parseBracketedList(ParsingContext.TypeArguments, ParseValue, 语法.less, 语法.greater);
            }
        }

        function 编译结构字面量() {
            const node = createNode(语法.结构字面量) as 结构字面量
            node.成员 = parseBracketedList(ParsingContext.ObjectLiteralMembers, ParseValue, 语法.l_brace, 语法.r_brace);
            return finishNode(node)
        }
        
        function 编译数组字面量() {
            const node = createNode(语法.数组字面量) as 数组字面量
            node.成员 = parseBracketedList(ParsingContext.ArrayLiteralMembers, ParseValue, 语法.l_square, 语法.r_square);
            return finishNode(node)
        }

        function ParseDagArgList() {
            const ns: 命名Dag[] = []
            const pos = scanner.getStartPos();
            while (true) {
                const node = createNode(语法.命名Dag) as 命名Dag;
                if (token() === 语法.美元) {
                    node.name = 编译变量名();
                }
                else {
                    node.dag = ParseValue()
                    if (parseOptional(语法.colon)) {
                        node.name = 编译变量名()
                    }
                }
                if (!parseOptional(语法.comma)) {
                    break
                }
                ns.push( finishNode(node));
            }
            return createNodeArray(ns, pos);
        }

        function 编译变量名() {
            parseExpected(语法.美元)
            return parseIdentifierName()
        }

        function 编译Dag成员() {
            const node = createNode(语法.命名Dag) as 命名Dag;
            node.dag = ParseValue();
            if (parseOptional(语法.colon)) {
                node.name = 编译变量名();
            }
            if (token() !== 语法.r_paren) {
                node.args = ParseDagArgList()
            }
            return finishNode(node);
        }

        function 编译Dag字面量() {
            const node = createNode(语法.Dag字面量) as Dag字面量
            node.成员 = parseBracketedList(ParsingContext.Arguments, 编译Dag成员, 语法.l_paren, 语法.r_paren);
            return finishNode(node);
        }

        function 编译名称引用() {
            const node = createNode(语法.名称引用) as 名称引用
            node.name = parseIdentifierName();
            node.类型参数 = 编译类型引用的类型参数();
            return finishNode(node);
        }

        function 类型引用节点(): TypeReferenceNode {
            const node = createNode(语法.类型引用节点) as TypeReferenceNode
            node.typeName = parseIdentifierName();
            node.typeArguments = 编译类型引用的类型参数();
            return finishNode(node);
        }

        function 编译比特类型() {
            const node = createNode(语法.比特类型) as 比特类型
            parseExpected(语法.Bits);
            parseExpected(语法.less);
            node.位 = 编译数字字面量();
            parseExpected(语法.greater);
            return finishNode(node);
        }

        function 编译列表类型() {
            const node = createNode(语法.编译列表类型) as 编译列表类型
            parseExpected(语法.List);
            parseExpected(语法.less);
            node.成员 = ParseType();
            parseExpected(语法.greater);
            return finishNode(node);
        }

        function ParseType() {
            const kind = token();
            switch (kind) {
                case 语法.String:
                case 语法.Bit:
                case 语法.Int:
                case 语法.Dag:
                case 语法.Code:
                    return parseTokenNode() as 关键字类型节点
                case 语法.id:
                    return 类型引用节点();
                case 语法.Bits:
                    return 编译比特类型();
                case 语法.List:
                    return 编译列表类型();
                default: Debug.fail(""+scanner.getStartPos());
            }
        }

        function ParseOperatorType() {
            const node = createNode(语法.OperatorType) as OperatorType
            parseExpected(语法.less)
            node.type = ParseType()
            parseExpected(语法.greater)
            return finishNode(node);
        }

        function ParseOperationForEachFilter(node: 循环运算){            
            parseExpected(语法.l_paren);
            node.引用 = parseIdentifierName()
            parseExpected(语法.comma);
            node.value1 = ParseValue();
            parseExpected(语法.comma);            
            node.value2 = ParseValue();
            parseExpected(语法.r_paren);
            return finishNode(node)

        }

        function ParseOperationSubstr(node: XSubstr) {
            parseExpected(语法.l_paren);
            node.value1 = ParseValue();
            parseExpected(语法.comma);
            node.value2 = ParseValue();
            parseExpected(语法.r_paren);
            return finishNode(node)

        }

        function ParseOperationFind(node: XFind) {
            parseExpected(语法.l_paren);
            node.value1 = ParseValue();
            parseExpected(语法.comma);
            node.value2 = ParseValue();
            parseExpected(语法.r_paren);
            return finishNode(node)

        }

        function 编译值表达式() {
            const node = createNode(语法.值表达式) as 值表达式
            node.value1 = ParseValue();
            parseExpected(语法.colon);
            node.value2 = ParseValue();
            return finishNode(node)
        }

        function ParseOperationCond(node: XCond) {
            node.value1 = parseBracketedList(ParsingContext.Arguments, 编译值表达式, 语法.l_paren, 语法.r_paren);
            return finishNode(node)
        }        

        function ParseOperation() {
            const node = createNode(语法.Operation) as Operation;
            const code = parseTokenNode<Token>();
            node.code = code;
            switch (code.kind) {
                case 语法.XGetDagOp:
                case 语法.XCast:
                case 语法.XHead:
                case 语法.XTail:
                case 语法.XSize:
                case 语法.XEmpty:
                case 语法.XIsA: {
                    switch (code.kind) {
                        case 语法.XGetDagOp:
                            if (token() === 语法.less) {
                                node.type = ParseOperatorType();
                            }
                            break;
                        case 语法.XIsA:
                        case 语法.XCast:
                            node.type = ParseOperatorType();
                            break;
                    }
                    parseExpected(语法.l_paren);
                    node.value1 = ParseValue();
                    parseExpected(语法.r_paren);
                    return finishNode(node)
                }
                case 语法.XConcat:
                case 语法.XADD:
                case 语法.XSUB:
                case 语法.XMUL:
                case 语法.XNOT:
                case 语法.XAND:
                case 语法.XOR:
                case 语法.XXOR:
                case 语法.XSRA:
                case 语法.XSRL:
                case 语法.XSHL:
                case 语法.XEq:
                case 语法.XNe:
                case 语法.XLe:
                case 语法.XLt:
                case 语法.XGe:
                case 语法.XGt:
                case 语法.XListConcat:
                case 语法.XListSplat:
                case 语法.XStrConcat:
                case 语法.XInterleave:
                case 语法.XSetDagOp: {
                    parseExpected(语法.l_paren);
                    node.value1 = ParseValue();
                    parseExpected(语法.comma);
                    (<二元运算>node).value2 = ParseValue();
                    parseExpected(语法.r_paren);
                    return finishNode(node) as 二元运算
                }
                case 语法.XForEach:
                case 语法.XFilter:
                    return ParseOperationForEachFilter(node as 循环运算);
                case 语法.XDag:
                case 语法.XIf:
                case 语法.XSubst: {
                    parseExpected(语法.l_paren);
                    node.value1 = ParseValue();
                    parseExpected(语法.comma);
                    (<条件运算>node).value2 = ParseValue();
                    parseExpected(语法.comma);
                    (<条件运算>node).value3 = ParseValue();
                    parseExpected(语法.r_paren);
                    return finishNode(node) as 条件运算
                }
                case 语法.XCond:
                    return ParseOperationCond(node as any as XCond);
                case 语法.XSubstr:
                    return ParseOperationSubstr(node as XSubstr);
                case 语法.XFind:
                    return ParseOperationFind(node as XFind);
                case 语法.XFoldl: {                    
                    parseExpected(语法.l_paren);
                    (<XFoldl>node).value1 = ParseValue();
                    parseExpected(语法.comma);
                    (<XFoldl>node).value2 = ParseValue();   

                    parseExpected(语法.comma);
                    (<XFoldl>node).id1 = parseIdentifierName(); 
                    parseExpected(语法.comma);
                    (<XFoldl>node).id2 = parseIdentifierName(); 
                    parseExpected(语法.comma);
                    (<XFoldl>node).value3 = ParseValue();
                    parseExpected(语法.r_paren);
                    return finishNode(node) as XFoldl
                }
            }
            Debug.fail(""+scanner.getStartPos())
        }

        function ParseRangePiece() {
            const ra = createNode(语法.RangePiece) as RangePiece
            ra.value1 = ParseValue();
            const kind = token();
            switch (kind) {
                case 语法.dotdotdot:
                case 语法.minus:
                    ra.code = parseTokenNode();
                    ra.value2 = ParseValue();
                    break;
                case 语法.数组字面量:
                    ra.value2 = 编译数字字面量();
                    break;
            }
            return finishNode(ra);
        }
    
        function ParseRangeList(l: 语法, r: 语法) {
            let kind = ParsingContext.TypeArguments
            if (l === 语法.l_brace) {
                kind = ParsingContext.ObjectLiteralMembers
            }
            else if (l === 语法.l_square) {
                kind = ParsingContext.ArrayLiteralMembers
            }
            return parseBracketedList(kind, ParseRangePiece, l, r);
        }

        function ParseValue(编译名称 = false) {
            let Result = ParseSimpleValue() as Expression;
            Debug.assert(Result, ""+ scanner.getStartPos());
            while (true) {
                const kind = token();
                switch (kind) {
                    case 语法.l_brace: {
                        if(编译名称) return Result;
                        const node = createNode(语法.复合表达式, Result.pos) as 复合表达式
                        node.value1 = Result;
                        const ra = ParseRangeList(语法.l_brace, 语法.r_brace)
                        node.range = ra;
                        return finishNode(node);
                    }
                    case 语法.l_square:
                        {
                            const node = createNode(语法.复合表达式2, Result.pos) as 复合表达式2
                            node.value1 = Result;
                            const ra = ParseRangeList(语法.l_square, 语法.r_square)
                            node.range = ra;
                            Result = finishNode(node);
                            break;
                        }
                    case 语法.dot: {
                        parseExpected(语法.dot)
                        const name = parseIdentifierName()
                        const node = createNode(语法.属性访问表达式, Result.pos) as 属性访问表达式
                        node.value1 = Result
                        node.name = name;
                        Result = finishNode(node);
                        break;
                    }
                    case 语法.paste: {
                        parseExpected(语法.paste)
                        switch (token()) {
                            case 语法.colon:
                            case 语法.semi:
                            case 语法.l_brace:
                                break;
                            default:
                                const node = createNode(语法.复合表达式3, Result.pos) as 复合表达式3
                                node.value1 = Result
                                node.value2 = ParseValue();
                                Result = finishNode(node);
                                break;
                        }
                    }
                    default:
                        return Result
                }
            }
        }

        function ParseSimpleValue() {
            const kind = token();
            switch (kind) {
                case 语法.TrueKW:
                case 语法.FalseKW:
                    return parseTokenNode() as 关键字表达式
                case 语法.文本字面量:
                    return 编译文本字面量();
                case 语法.数字字面量:
                    return 编译数字字面量();
                case 语法.CodeFragment:
                    return 编译代码片段();
                case 语法.question:
                    return parseTokenNode() as 关键字表达式
                case 语法.id:
                    return 编译名称引用();
                case 语法.l_brace:
                    return 编译结构字面量();
                case 语法.l_square:
                    return 编译数组字面量();
                case 语法.l_paren:
                    return 编译Dag字面量();
                case 语法.minus:
                case 语法.plus:
                    return 编译正负号数字()
                case 语法.XHead:
                case 语法.XTail:
                case 语法.XSize:
                case 语法.XEmpty:
                case 语法.XCast:
                case 语法.XGetDagOp:
                case 语法.XIsA:
                case 语法.XConcat:
                case 语法.XDag:
                case 语法.XADD:
                case 语法.XSUB:
                case 语法.XMUL:
                case 语法.XNOT:
                case 语法.XAND:
                case 语法.XOR:
                case 语法.XXOR:
                case 语法.XSRA:
                case 语法.XSRL:
                case 语法.XSHL:
                case 语法.XEq:
                case 语法.XNe:
                case 语法.XLe:
                case 语法.XLt:
                case 语法.XGe:
                case 语法.XGt:
                case 语法.XListConcat:
                case 语法.XListSplat:
                case 语法.XStrConcat:
                case 语法.XInterleave:
                case 语法.XSetDagOp:
                case 语法.XIf:
                case 语法.XCond:
                case 语法.XFoldl:
                case 语法.XForEach:
                case 语法.XFilter:
                case 语法.XSubst:
                case 语法.XSubstr:
                case 语法.XFind:
                    return ParseOperation()
            }
            return;
        }

        function ParseSubClassReference(是多类 = false) {
            return 编译名称引用()
        }

        function ParseObjectName() {
            const kind = token()
            switch (kind) {
                case 语法.colon:
                case 语法.semi:
                case 语法.l_brace:
                    return;
                default:
                    break;
            }
            return ParseValue(true);
        }

        function createNodeArray<T>(arr: T[], pos?: number, end?: number) {
            (arr as NodeArray<T>).pos = pos;
            (arr as NodeArray<T>).end = end || scanner.getStartPos();
            return arr as NodeArray<T>;
        }

        function ParseDefvar() {
            const node = createNode(语法.声明变量) as 声明变量
            parseExpected(语法.Defvar);
            node.name = parseIdentifierName();
            parseExpected(语法.equal);
            node.value1 = ParseValue();
            finishNode(node)
            parseSemicolon();
            return node;
        }
        
        function ParseTypeParamDeclaration() {
            const node = createNode(语法.声明) as 声明
            if (token() === 语法.Field) {
                node.Field = parseTokenNode()
            }
            node.type = ParseType()
            node.name = parseIdentifierName()
            if (parseOptional(语法.equal)) {
                node.value1 = ParseValue()
            }
            finishNode(node)
            return node
        }

        function ParseDeclaration() {
            const node = createNode(语法.声明) as 声明
            if (token() === 语法.Field) {
                node.Field = parseTokenNode()
            }
            node.type = ParseType()
            node.name = parseIdentifierName()
            if (parseOptional(语法.equal)) {
                node.value1 = ParseValue()
            }
            finishNode(node)
            parseSemicolon();
            return node
        }

        function ParseOptionalBitList() {
            if (token() === 语法.l_brace) {
               return ParseRangeList(语法.l_brace, 语法.r_brace)
            }
            return;
        }

        function ParseBodyItem() {
            const kind = token()
            if (kind === 语法.Assert) {
                return 编译断言语句();
            }
            else if (kind === 语法.Defvar) {
                return ParseDefvar();
            }
            else {
                if (kind !== 语法.Let) {
                    return ParseDeclaration();
                }
                const node = createNode(语法.声明) as 声明
                parseTokenNode() // 语法.let
                node.name = parseIdentifierName();
                node.reange = ParseOptionalBitList();
                parseExpected(语法.equal)
                node.value1 = ParseValue();
                finishNode(node);
                parseSemicolon();
                return node;
            }
        }

        function ParseBody(node: 定义 | 类声明) {
            if (parseOptional(语法.semi)) {
                return;
            }
            node.元素 = 编译非逗号列表(ParsingContext.ObjectLiteralMembers, ParseBodyItem, 语法.l_brace, 语法.r_brace);
        }

        function ParseObjectBody(obj: 定义 | 类声明) {
            if (parseOptional(语法.colon)) {
                const su = ParseSubClassReference();
                const sus = [su];
                while (true) {
                    if (parseOptional(语法.comma)) {
                        sus.push(ParseSubClassReference());
                    }
                    break;
                }
                obj.sub = createNodeArray(sus, su.pos);
            }
            ParseBody(obj);
        }

        function ParseDef() {
            const node = createNode(语法.定义) as 定义
            parseExpected(语法.Def)
            node.name = ParseObjectName();
            ParseObjectBody(node);
            finishNode(node);
            parseSemicolon();
            return node;
        }

        function ParseTemplateArgList() {
           return  parseBracketedList(ParsingContext.TypeParameters, ParseTypeParamDeclaration, 语法.less, 语法.greater)
        }

        function ParseClass() {
            const node = createNode(语法.类声明) as 类声明
            parseExpected(语法.Class)
            node.name = parseIdentifierName()
            if (token() === 语法.less) {
                node.类型参数 = ParseTemplateArgList();
            }
            ParseObjectBody(node)
            finishNode(node)
            parseSemicolon()
            return node;
        }

        function ParseMultiClass() {
            const node = createNode(语法.多类声明) as 类声明
            parseExpected(语法.MultiClass);
            node.name = parseIdentifierName()
            if (token() === 语法.less) {
                node.类型参数 = ParseTemplateArgList();
            }
            let 有子 = false;
            if (parseOptional(语法.colon)) {
                const su = ParseSubClassReference();
                const sus = [su]
                while (true) {
                    if (parseOptional(语法.comma)) {
                        sus.push(ParseSubClassReference())
                    }
                }
                有子 = true;
                node.sub = createNodeArray(sus, su.pos);
            }
            if (token() !== 语法.l_brace) {
                if (!有子) {
                    Debug.fail(""+scanner.getStartPos())
                }
                finishNode(node)
                parseExpected(语法.semi);
                return node;
            }
            else {
                parseExpected(语法.l_brace);
                if (token() === 语法.r_brace) Debug.fail(""+scanner.getStartPos());
                const bs: Node[] = []
                while (token() !== 语法.r_brace) {
                    switch (token()) {
                        default:
                            return Debug.fail(""+scanner.getStartPos())
                        case 语法.Assert:
                        case 语法.Def:
                        case 语法.Defm:
                        case 语法.Defvar:
                        case 语法.Foreach:
                        case 语法.If:
                        case 语法.Let:
                            bs.push(ParseObject())
                            break;
                    }
                }
                node.元素 = createNodeArray(bs);
                parseExpected(语法.r_brace);
                finishNode(node);
                parseSemicolon();
                return node;
            }
        }

        function ParseDefset() {
            const node = createNode(语法.定义集合) as 定义集合
            parseExpected(语法.Defset);
            node.type = ParseType();
            node.name = parseIdentifierName();
            parseExpected(语法.equal);
            node.元素 = 编译非逗号列表(ParsingContext.TypeParameters, ParseObject, 语法.l_brace, 语法.r_brace)
            finishNode(node);
            parseSemicolon();
            return node
        }

        function ParseDefm() {
            const node = createNode(语法.多定义) as 多定义
            parseExpected(语法.Defm);
            node.name = ParseObjectName()
            parseExpected(语法.colon)
            if (token() === 语法.id) {
                const su = ParseSubClassReference();
                const sus = [su];
                while (true) {
                    if (!su) {
                        break;
                    }
                    if (!parseOptional(语法.comma)) {
                        break;
                    }
                    sus.push(ParseSubClassReference());
                }
                node.sub = createNodeArray(sus)
            }

            finishNode(node)
            parseSemicolon();
            return node
        }

        function ParseIfBody(kind: 语法) {
            parseExpected(kind)
            if (token() !== 语法.l_brace) {
                return ParseObject()
            }
            else {
                parseExpected(语法.l_brace);
                return ParseObjectList()
                parseExpected(语法.r_brace);
            }
        }

        function ParseIf() {
            const node = createNode(语法.if语句) as if语句
            parseExpected(语法.If);
            node.条件  = ParseValue();
            Debug.assert(token() !== 语法.Then)
            node.thenB = ParseIfBody(语法.Then)
            if (parseOptional(语法.ElseKW)) {
                node.elseB = ParseIfBody(语法.Else)
            }
            finishNode(node)
            parseSemicolon()
            return node;            
        }

        function ParseForeachDeclaration() {
            const node = createNode(语法.循环声明) as 循环声明
            node.name = parseIdentifierName();
            parseExpected(语法.equal);
            switch (token()) {
                case 语法.l_brace:
                    node.range = ParseRangeList(语法.l_brace, 语法.r_brace)
                default:
                    node.ex = ParseValue()
                    if (token() === 语法.l_brace) {
                        node.range = ParseRangeList(语法.l_brace, 语法.r_brace)
                    }
            }
            finishNode(node);
            parseSemicolon();
            return node;

        }

        function ParseForeach() {
            const node = createNode(语法.循环语句) as 循环语句;
            parseExpected(语法.Foreach);
            node.dec = ParseForeachDeclaration();

            parseExpected(语法.In)
            if (token() !== 语法.l_brace) {
                node.sta = ParseObject();
            }
            else {
                parseExpected(语法.l_brace);
                node.sta = ParseObjectList();
                parseExpected(语法.r_brace);
            }
            finishNode(node);
            parseSemicolon();
            return node;
        }

        function ParseLetList() {
            const ns: 变量列表成员[] = [];
            do {
                const node = createNode(语法.变量列表成员) as 变量列表成员
                node.name = parseIdentifierName()
                if (token() === 语法.less) {
                    node.range =  ParseRangeList(语法.less, 语法.greater)
                }
                parseExpected(语法.equal)
                node.value = ParseValue()
                ns.push(node)
            }
            while(parseOptional(语法.comma))
            return createNodeArray(ns)
        }

        function ParseTopLevelLet() {

            const node = createNode(语法.顶级变量) as 顶级变量;
            parseExpected(语法.Let);
            node.letList = ParseLetList()
            parseExpected(语法.In)
            if (token() !== 语法.l_brace) {
                node.objs = ParseObject()
            }
            else {
                parseExpected(语法.l_brace);
                node.objs =  ParseObjectList()
                parseExpected(语法.r_brace);
            }
            finishNode(node);
            parseSemicolon();
            return node;
        }        

        function ParseObject() {
            const kind = token();
            switch (kind) {
                case 语法.Include:
                    return 编译包含语句();
                case 语法.Assert:
                    return 编译断言语句();
                case 语法.Def:
                    return ParseDef();
                case 语法.Defm:
                    return ParseDefm();
                case 语法.Defvar:
                    return ParseDefvar()
                case 语法.Foreach:
                    return ParseForeach()
                case 语法.If:
                    return ParseIf();
                case 语法.Let:
                    return ParseTopLevelLet()
                case 语法.Defset:
                    return ParseDefset()
                case 语法.Class:
                    return ParseClass();
                case 语法.MultiClass:
                    return ParseMultiClass();
                case 语法.Ifdef:
                case 语法.Ifndef:
                case 语法.Define:
                    Debug.fail("发现预处理")
            }
            return createNode(语法.Error, getNodePos()) as Statement;
        }

    }

    namespace IncrementalParser {
        export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile {
            aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive);

            checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks);
            if (textChangeRangeIsUnchanged(textChangeRange)) {
                // if the text didn't change, then we can just return our current source file as-is.
                return sourceFile;
            }

            if (sourceFile.statements.length === 0) {
                // If we don't have any statements in the current source file, then there's no real
                // way to incrementally parse.  So just do a full parse instead.
                return Parser.parseSourceFile(sourceFile.fileName, newText, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind);
            }

            // Make sure we're not trying to incrementally update a source file more than once.  Once
            // we do an update the original source file is considered unusable from that point onwards.
            //
            // This is because we do incremental parsing in-place.  i.e. we take nodes from the old
            // tree and give them new positions and parents.  From that point on, trusting the old
            // tree at all is not possible as far too much of it may violate invariants.
            const incrementalSourceFile = <IncrementalNode><Node>sourceFile;
            Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed);
            incrementalSourceFile.hasBeenIncrementallyParsed = true;

            const oldText = sourceFile.text;
            const syntaxCursor = createSyntaxCursor(sourceFile);

            // Make the actual change larger so that we know to reparse anything whose lookahead
            // might have intersected the change.
            const changeRange = extendToAffectedRange(sourceFile, textChangeRange);
            checkChangeRange(sourceFile, newText, changeRange, aggressiveChecks);

            // Ensure that extending the affected range only moved the start of the change range
            // earlier in the file.
            Debug.assert(changeRange.span.start <= textChangeRange.span.start);
            Debug.assert(textSpanEnd(changeRange.span) === textSpanEnd(textChangeRange.span));
            Debug.assert(textSpanEnd(textChangeRangeNewSpan(changeRange)) === textSpanEnd(textChangeRangeNewSpan(textChangeRange)));

            // The is the amount the nodes after the edit range need to be adjusted.  It can be
            // positive (if the edit added characters), negative (if the edit deleted characters)
            // or zero (if this was a pure overwrite with nothing added/removed).
            const delta = textChangeRangeNewSpan(changeRange).length - changeRange.span.length;

            // If we added or removed characters during the edit, then we need to go and adjust all
            // the nodes after the edit.  Those nodes may move forward (if we inserted chars) or they
            // may move backward (if we deleted chars).
            //
            // Doing this helps us out in two ways.  First, it means that any nodes/tokens we want
            // to reuse are already at the appropriate position in the new text.  That way when we
            // reuse them, we don't have to figure out if they need to be adjusted.  Second, it makes
            // it very easy to determine if we can reuse a node.  If the node's position is at where
            // we are in the text, then we can reuse it.  Otherwise we can't.  If the node's position
            // is ahead of us, then we'll need to rescan tokens.  If the node's position is behind
            // us, then we'll need to skip it or crumble it as appropriate
            //
            // We will also adjust the positions of nodes that intersect the change range as well.
            // By doing this, we ensure that all the positions in the old tree are consistent, not
            // just the positions of nodes entirely before/after the change range.  By being
            // consistent, we can then easily map from positions to nodes in the old tree easily.
            //
            // Also, mark any syntax elements that intersect the changed span.  We know, up front,
            // that we cannot reuse these elements.
            updateTokenPositionsAndMarkElements(incrementalSourceFile,
                changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);

            // Now that we've set up our internal incremental state just proceed and parse the
            // source file in the normal fashion.  When possible the parser will retrieve and
            // reuse nodes from the old tree.
            //
            // Note: passing in 'true' for setNodeParents is very important.  When incrementally
            // parsing, we will be reusing nodes from the old tree, and placing it into new
            // parents.  If we don't set the parents now, we'll end up with an observably
            // inconsistent tree.  Setting the parents on the new tree should be very fast.  We
            // will immediately bail out of walking any subtrees when we can see that their parents
            // are already correct.
            const result = Parser.parseSourceFile(sourceFile.fileName, newText, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind);

            return result;
        }

        function shouldCheckNode(node: Node) {
            switch (node.kind) {
                case 语法.String:
                case 语法.数字字面量:
                case 语法.id:
                    return true;
            }

            return false;
        }

        function moveElementEntirelyPastChangeRange(element: IncrementalElement, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
            if (isArray) {
                visitArray(<IncrementalNodeArray>element);
            }
            else {
                visitNode(<IncrementalNode>element);
            }
            return;

            function visitNode(node: IncrementalNode) {
                let text = "";
                if (aggressiveChecks && shouldCheckNode(node)) {
                    text = oldText.substring(node.pos, node.end);
                }

                // Ditch any existing LS children we may have created.  This way we can avoid
                // moving them forward.
                if (node._children) {
                    node._children = undefined;
                }

                node.pos += delta;
                node.end += delta;

                if (aggressiveChecks && shouldCheckNode(node)) {
                    Debug.assert(text === newText.substring(node.pos, node.end));
                }

                forEachChild(node, visitNode, visitArray);
                if (node.jsDocComments) {
                    for (const jsDocComment of node.jsDocComments) {
                        forEachChild(jsDocComment, visitNode, visitArray);
                    }
                }
                checkNodePositions(node, aggressiveChecks);
            }

            function visitArray(array: IncrementalNodeArray) {
                array._children = undefined;
                array.pos += delta;
                array.end += delta;

                for (const node of array) {
                    visitNode(node);
                }
            }
        }

        function adjustIntersectingElement(element: IncrementalElement, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
            Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range");
            Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range");
            Debug.assert(element.pos <= element.end);

            // We have an element that intersects the change range in some way.  It may have its
            // start, or its end (or both) in the changed range.  We want to adjust any part
            // that intersects such that the final tree is in a consistent state.  i.e. all
            // children have spans within the span of their parent, and all siblings are ordered
            // properly.

            // We may need to update both the 'pos' and the 'end' of the element.

            // If the 'pos' is before the start of the change, then we don't need to touch it.
            // If it isn't, then the 'pos' must be inside the change.  How we update it will
            // depend if delta is  positive or negative.  If delta is positive then we have
            // something like:
            //
            //  -------------------AAA-----------------
            //  -------------------BBBCCCCCCC-----------------
            //
            // In this case, we consider any node that started in the change range to still be
            // starting at the same position.
            //
            // however, if the delta is negative, then we instead have something like this:
            //
            //  -------------------XXXYYYYYYY-----------------
            //  -------------------ZZZ-----------------
            //
            // In this case, any element that started in the 'X' range will keep its position.
            // However any element that started after that will have their pos adjusted to be
            // at the end of the new range.  i.e. any node that started in the 'Y' range will
            // be adjusted to have their start at the end of the 'Z' range.
            //
            // The element will keep its position if possible.  Or Move backward to the new-end
            // if it's in the 'Y' range.
            element.pos = Math.min(element.pos, changeRangeNewEnd);

            // If the 'end' is after the change range, then we always adjust it by the delta
            // amount.  However, if the end is in the change range, then how we adjust it
            // will depend on if delta is  positive or negative.  If delta is positive then we
            // have something like:
            //
            //  -------------------AAA-----------------
            //  -------------------BBBCCCCCCC-----------------
            //
            // In this case, we consider any node that ended inside the change range to keep its
            // end position.
            //
            // however, if the delta is negative, then we instead have something like this:
            //
            //  -------------------XXXYYYYYYY-----------------
            //  -------------------ZZZ-----------------
            //
            // In this case, any element that ended in the 'X' range will keep its position.
            // However any element that ended after that will have their pos adjusted to be
            // at the end of the new range.  i.e. any node that ended in the 'Y' range will
            // be adjusted to have their end at the end of the 'Z' range.
            if (element.end >= changeRangeOldEnd) {
                // Element ends after the change range.  Always adjust the end pos.
                element.end += delta;
            }
            else {
                // Element ends in the change range.  The element will keep its position if
                // possible. Or Move backward to the new-end if it's in the 'Y' range.
                element.end = Math.min(element.end, changeRangeNewEnd);
            }

            Debug.assert(element.pos <= element.end);
            if (element.parent) {
                Debug.assert(element.pos >= element.parent.pos);
                Debug.assert(element.end <= element.parent.end);
            }
        }

        function checkNodePositions(node: Node, aggressiveChecks: boolean) {
            if (aggressiveChecks) {
                let pos = node.pos;
                forEachChild(node, child => {
                    Debug.assert(child.pos >= pos);
                    pos = child.end;
                });
                Debug.assert(pos <= node.end);
            }
        }

        function updateTokenPositionsAndMarkElements(
            sourceFile: IncrementalNode,
            changeStart: number,
            changeRangeOldEnd: number,
            changeRangeNewEnd: number,
            delta: number,
            oldText: string,
            newText: string,
            aggressiveChecks: boolean): void {

            visitNode(sourceFile);
            return;

            function visitNode(child: IncrementalNode) {
                Debug.assert(child.pos <= child.end);
                if (child.pos > changeRangeOldEnd) {
                    // Node is entirely past the change range.  We need to move both its pos and
                    // end, forward or backward appropriately.
                    moveElementEntirelyPastChangeRange(child, /*isArray*/ false, delta, oldText, newText, aggressiveChecks);
                    return;
                }

                // Check if the element intersects the change range.  If it does, then it is not
                // reusable.  Also, we'll need to recurse to see what constituent portions we may
                // be able to use.
                const fullEnd = child.end;
                if (fullEnd >= changeStart) {
                    child.intersectsChange = true;
                    child._children = undefined;

                    // Adjust the pos or end (or both) of the intersecting element accordingly.
                    adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
                    forEachChild(child, visitNode, visitArray);

                    checkNodePositions(child, aggressiveChecks);
                    return;
                }

                // Otherwise, the node is entirely before the change range.  No need to do anything with it.
                Debug.assert(fullEnd < changeStart);
            }

            function visitArray(array: IncrementalNodeArray) {
                Debug.assert(array.pos <= array.end);
                if (array.pos > changeRangeOldEnd) {
                    // Array is entirely after the change range.  We need to move it, and move any of
                    // its children.
                    moveElementEntirelyPastChangeRange(array, /*isArray*/ true, delta, oldText, newText, aggressiveChecks);
                    return;
                }

                // Check if the element intersects the change range.  If it does, then it is not
                // reusable.  Also, we'll need to recurse to see what constituent portions we may
                // be able to use.
                const fullEnd = array.end;
                if (fullEnd >= changeStart) {
                    array.intersectsChange = true;
                    array._children = undefined;

                    // Adjust the pos or end (or both) of the intersecting array accordingly.
                    adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
                    for (const node of array) {
                        visitNode(node);
                    }
                    return;
                }

                // Otherwise, the array is entirely before the change range.  No need to do anything with it.
                Debug.assert(fullEnd < changeStart);
            }
        }

        function extendToAffectedRange(sourceFile: SourceFile, changeRange: TextChangeRange): TextChangeRange {
            // Consider the following code:
            //      void foo() { /; }
            //
            // If the text changes with an insertion of / just before the semicolon then we end up with:
            //      void foo() { //; }
            //
            // If we were to just use the changeRange a is, then we would not rescan the { token
            // (as it does not intersect the actual original change range).  Because an edit may
            // change the token touching it, we actually need to look back *at least* one token so
            // that the prior token sees that change.
            const maxLookahead = 1;

            let start = changeRange.span.start;

            // the first iteration aligns us with the change start. subsequent iteration move us to
            // the left by maxLookahead tokens.  We only need to do this as long as we're not at the
            // start of the tree.
            for (let i = 0; start > 0 && i <= maxLookahead; i++) {
                const nearestNode = findNearestNodeStartingBeforeOrAtPosition(sourceFile, start);
                Debug.assert(nearestNode.pos <= start);
                const position = nearestNode.pos;

                start = Math.max(0, position - 1);
            }

            const finalSpan = createTextSpanFromBounds(start, textSpanEnd(changeRange.span));
            const finalLength = changeRange.newLength + (changeRange.span.start - start);

            return createTextChangeRange(finalSpan, finalLength);
        }

        function findNearestNodeStartingBeforeOrAtPosition(sourceFile: SourceFile, position: number): Node {
            let bestResult: Node = sourceFile;
            let lastNodeEntirelyBeforePosition: Node;

            forEachChild(sourceFile, visit);

            if (lastNodeEntirelyBeforePosition) {
                const lastChildOfLastEntireNodeBeforePosition = getLastChild(lastNodeEntirelyBeforePosition);
                if (lastChildOfLastEntireNodeBeforePosition.pos > bestResult.pos) {
                    bestResult = lastChildOfLastEntireNodeBeforePosition;
                }
            }

            return bestResult;

            function getLastChild(node: Node): Node {
                while (true) {
                    const lastChild = getLastChildWorker(node);
                    if (lastChild) {
                        node = lastChild;
                    }
                    else {
                        return node;
                    }
                }
            }

            function getLastChildWorker(node: Node): Node {
                let last: Node = undefined;
                forEachChild(node, child => {
                    if (nodeIsPresent(child)) {
                        last = child;
                    }
                });
                return last;
            }

            function visit(child: Node) {
                if (nodeIsMissing(child)) {
                    // Missing nodes are effectively invisible to us.  We never even consider them
                    // When trying to find the nearest node before us.
                    return;
                }

                // If the child intersects this position, then this node is currently the nearest
                // node that starts before the position.
                if (child.pos <= position) {
                    if (child.pos >= bestResult.pos) {
                        // This node starts before the position, and is closer to the position than
                        // the previous best node we found.  It is now the new best node.
                        bestResult = child;
                    }

                    // Now, the node may overlap the position, or it may end entirely before the
                    // position.  If it overlaps with the position, then either it, or one of its
                    // children must be the nearest node before the position.  So we can just
                    // recurse into this child to see if we can find something better.
                    if (position < child.end) {
                        // The nearest node is either this child, or one of the children inside
                        // of it.  We've already marked this child as the best so far.  Recurse
                        // in case one of the children is better.
                        forEachChild(child, visit);

                        // Once we look at the children of this node, then there's no need to
                        // continue any further.
                        return true;
                    }
                    else {
                        Debug.assert(child.end <= position);
                        // The child ends entirely before this position.  Say you have the following
                        // (where $ is the position)
                        //
                        //      <complex expr 1> ? <complex expr 2> $ : <...> <...>
                        //
                        // We would want to find the nearest preceding node in "complex expr 2".
                        // To support that, we keep track of this node, and once we're done searching
                        // for a best node, we recurse down this node to see if we can find a good
                        // result in it.
                        //
                        // This approach allows us to quickly skip over nodes that are entirely
                        // before the position, while still allowing us to find any nodes in the
                        // last one that might be what we want.
                        lastNodeEntirelyBeforePosition = child;
                    }
                }
                else {
                    Debug.assert(child.pos > position);
                    // We're now at a node that is entirely past the position we're searching for.
                    // This node (and all following nodes) could never contribute to the result,
                    // so just skip them by returning 'true' here.
                    return true;
                }
            }
        }

        function checkChangeRange(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean) {
            const oldText = sourceFile.text;
            if (textChangeRange) {
                Debug.assert((oldText.length - textChangeRange.span.length + textChangeRange.newLength) === newText.length);

                if (aggressiveChecks || Debug.shouldAssert(AssertionLevel.VeryAggressive)) {
                    const oldTextPrefix = oldText.substr(0, textChangeRange.span.start);
                    const newTextPrefix = newText.substr(0, textChangeRange.span.start);
                    Debug.assert(oldTextPrefix === newTextPrefix);

                    const oldTextSuffix = oldText.substring(textSpanEnd(textChangeRange.span), oldText.length);
                    const newTextSuffix = newText.substring(textSpanEnd(textChangeRangeNewSpan(textChangeRange)), newText.length);
                    Debug.assert(oldTextSuffix === newTextSuffix);
                }
            }
        }

        interface IncrementalElement extends TextRange {
            parent?: Node;
            intersectsChange: boolean;
            length?: number;
            _children: Node[];
        }

        export interface IncrementalNode extends Node, IncrementalElement {
            hasBeenIncrementallyParsed: boolean;
        }

        interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement {
            length: number;
        }

        // Allows finding nodes in the source file at a certain position in an efficient manner.
        // The implementation takes advantage of the calling pattern it knows the parser will
        // make in order to optimize finding nodes as quickly as possible.
        export interface SyntaxCursor {
            currentNode(position: number): IncrementalNode;
        }

        function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor {
            let currentArray: NodeArray<Node> = sourceFile.statements;
            let currentArrayIndex = 0;

            Debug.assert(currentArrayIndex < currentArray.length);
            let current = currentArray[currentArrayIndex];
            let lastQueriedPosition = InvalidPosition.Value;

            return {
                currentNode(position: number) {
                    // Only compute the current node if the position is different than the last time
                    // we were asked.  The parser commonly asks for the node at the same position
                    // twice.  Once to know if can read an appropriate list element at a certain point,
                    // and then to actually read and consume the node.
                    if (position !== lastQueriedPosition) {
                        // Much of the time the parser will need the very next node in the array that
                        // we just returned a node from.So just simply check for that case and move
                        // forward in the array instead of searching for the node again.
                        if (current && current.end === position && currentArrayIndex < (currentArray.length - 1)) {
                            currentArrayIndex++;
                            current = currentArray[currentArrayIndex];
                        }

                        // If we don't have a node, or the node we have isn't in the right position,
                        // then try to find a viable node at the position requested.
                        if (!current || current.pos !== position) {
                            findHighestListElementThatStartsAtPosition(position);
                        }
                    }

                    // Cache this query so that we don't do any extra work if the parser calls back
                    // into us.  Note: this is very common as the parser will make pairs of calls like
                    // 'isListElement -> parseListElement'.  If we were unable to find a node when
                    // called with 'isListElement', we don't want to redo the work when parseListElement
                    // is called immediately after.
                    lastQueriedPosition = position;

                    // Either we don'd have a node, or we have a node at the position being asked for.
                    Debug.assert(!current || current.pos === position);
                    return <IncrementalNode>current;
                }
            };

            // Finds the highest element in the tree we can find that starts at the provided position.
            // The element must be a direct child of some node list in the tree.  This way after we
            // return it, we can easily return its next sibling in the list.
            function findHighestListElementThatStartsAtPosition(position: number) {
                // Clear out any cached state about the last node we found.
                currentArray = undefined;
                currentArrayIndex = InvalidPosition.Value;
                current = undefined;

                // Recurse into the source file to find the highest node at this position.
                forEachChild(sourceFile, visitNode, visitArray);
                return;

                function visitNode(node: Node) {
                    if (position >= node.pos && position < node.end) {
                        // Position was within this node.  Keep searching deeper to find the node.
                        forEachChild(node, visitNode, visitArray);

                        // don't proceed any further in the search.
                        return true;
                    }

                    // position wasn't in this node, have to keep searching.
                    return false;
                }

                function visitArray(array: NodeArray<Node>) {
                    if (position >= array.pos && position < array.end) {
                        // position was in this array.  Search through this array to see if we find a
                        // viable element.
                        for (let i = 0, n = array.length; i < n; i++) {
                            const child = array[i];
                            if (child) {
                                if (child.pos === position) {
                                    // Found the right node.  We're done.
                                    currentArray = array;
                                    currentArrayIndex = i;
                                    current = child;
                                    return true;
                                }
                                else {
                                    if (child.pos < position && position < child.end) {
                                        // Position in somewhere within this child.  Search in it and
                                        // stop searching in this array.
                                        forEachChild(child, visitNode, visitArray);
                                        return true;
                                    }
                                }
                            }
                        }
                    }

                    // position wasn't in this array, have to keep searching.
                    return false;
                }
            }
        }

        const enum InvalidPosition {
            Value = -1
        }
    }
}
